package com.qf.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.qf.entity.product.ProductReviewLikeDislike;
import com.qf.mapper.ProductReviewLikeDislikeMapper;
import com.qf.mapper.ProductReviewMapper;
import com.qf.param.BO.ReviewLikeDislikeUpdateBO;
import com.qf.param.DTO.ProductLikeOrDislikeDTO;
import com.qf.redis.RedisUtil;
import com.qf.service.ProductReviewLikeDislikeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Author Ayases
 * @Date 2025/6/11 20:53
 * @Desc
 */
@Service
public class ProductReviewLikeDislikeServiceImpl implements ProductReviewLikeDislikeService {

    private static final Logger log = LoggerFactory.getLogger(ProductServiceImpl.class); // 声明 log 对象


    @Resource
    private ProductReviewLikeDislikeMapper productReviewLikeDislikeMapper;

    @Resource
    private ProductReviewMapper productReviewMapper;

    @Resource
    private RedisUtil redisUtil;

    /**
     * 点赞或点踩评论(点击/取消)
     * 逻辑：
     * 1. 查询数据库中当前用户对该评论的点赞/点踩记录
     * 2. 情况1：用户第一次点赞/点踩该评论-添加数据库记录，Redis缓存数+1
     * 3. 情况2：用户已经点赞/点踩过该评论
     * a. 状态为有效(status=1)：
     * - 操作类型相同：取消操作（status=0），Redis缓存数-1
     * - 操作类型不同：切换操作类型，Redis缓存数联动调整
     * b. 状态为已取消(status=0)：
     * - 重新激活记录（status=1），更新操作类型，Redis缓存数+1
     * 4. 支持无限次点击操作，状态正确转换
     *
     * @param productLikeOrDislikeDTO
     */
    @Override
    public void likeOrDislikeComment(ProductLikeOrDislikeDTO productLikeOrDislikeDTO) {
        // 构建Redis键：格式为"likeNum:yyyyMMdd"
        // 按天存储帖子点赞数量统计（避免单个key过大）
        String today = DateUtil.format(new Date(), "yyyyMMdd");
        String likeKey = String.format("comment_like:%s", today);   // 评论点赞Key
        String dislikeKey = String.format("comment_dislike:%s", today); // 评论点踩Key

        // 获取当前时间（GMT+8）
        LocalDateTime now = LocalDateTime.now(ZoneId.of("GMT+8"));

        // 查询数据库中当前用户对该评论的点赞/点踩记录（包含历史记录）
        ProductReviewLikeDislike likeRecord = productReviewLikeDislikeMapper.selectOne(
                new LambdaQueryWrapper<ProductReviewLikeDislike>()
                        .eq(ProductReviewLikeDislike::getReviewId, productLikeOrDislikeDTO.getReviewId())
                        .eq(ProductReviewLikeDislike::getUserId, productLikeOrDislikeDTO.getUserId())
        );

        // 情况1：用户第一次点赞/点踩该评论-添加数据库记录，Redis缓存数+1
        if (likeRecord == null) {
            // 构建点赞/点踩记录
            ProductReviewLikeDislike likeDislikeRecord = new ProductReviewLikeDislike();
            // 生成UUID的主键
            likeDislikeRecord.setId(IdUtil.simpleUUID());
            // 设置其他属性
            likeDislikeRecord.setReviewId(productLikeOrDislikeDTO.getReviewId());
            likeDislikeRecord.setUserId(productLikeOrDislikeDTO.getUserId());
            likeDislikeRecord.setActionType(productLikeOrDislikeDTO.getActionType());
            likeDislikeRecord.setStatus(1); // 初始状态：有效
            likeDislikeRecord.setCreatedAt(now);
            likeDislikeRecord.setUpdatedAt(now);

            // 保存点赞/点踩记录（插入数据库）
            productReviewLikeDislikeMapper.insert(likeDislikeRecord);

            // Redis缓存数更新评论点赞/点踩数量
            if (productLikeOrDislikeDTO.getActionType() == 1) {
                // 点赞
                redisUtil.incrCacheMapValue(likeKey, productLikeOrDislikeDTO.getReviewId(), 1);
            } else if (productLikeOrDislikeDTO.getActionType() == 0) {
                // 点踩
                redisUtil.incrCacheMapValue(dislikeKey, productLikeOrDislikeDTO.getReviewId(), 1);
            } else {
                // 其他类型（不处理）,抛出异常
                throw new RuntimeException("actionType参数错误");
            }
        } else {
            // 情况2：用户已经点赞/点踩过该评论

            // 当前操作类型
            Integer currentAction = productLikeOrDislikeDTO.getActionType();
            // 历史操作类型
            Integer historyAction = likeRecord.getActionType();
            // 历史状态
            Integer historyStatus = likeRecord.getStatus();

            // 场景2.1：历史记录状态为有效（status=1）
            if (historyStatus == 1) {
                // 操作类型相同：取消操作
                if (historyAction == currentAction) {
                    // 更新记录状态为已取消
                    likeRecord.setStatus(0);
                    likeRecord.setUpdatedAt(now);
                    productReviewLikeDislikeMapper.updateById(likeRecord);

                    // Redis缓存数更新：当前操作类型计数-1
                    if (currentAction == 1) {
                        redisUtil.incrCacheMapValue(likeKey, productLikeOrDislikeDTO.getReviewId(), -1);
                    } else if (currentAction == 0) {
                        redisUtil.incrCacheMapValue(dislikeKey, productLikeOrDislikeDTO.getReviewId(), -1);
                    } else {
                        throw new RuntimeException("actionType参数错误");
                    }
                }
                // 操作类型不同：切换操作
                else {
                    // 更新记录的操作类型
                    likeRecord.setActionType(currentAction);
                    likeRecord.setUpdatedAt(now);
                    productReviewLikeDislikeMapper.updateById(likeRecord);

                    // Redis缓存数更新：旧类型-1，新类型+1
                    if (currentAction == 1) {
                        // 切换为点赞：点赞+1，点踩-1
                        redisUtil.incrCacheMapValue(likeKey, productLikeOrDislikeDTO.getReviewId(), 1);
                        redisUtil.incrCacheMapValue(dislikeKey, productLikeOrDislikeDTO.getReviewId(), -1);
                    } else if (currentAction == 0) {
                        // 切换为点踩：点赞-1，点踩+1
                        redisUtil.incrCacheMapValue(likeKey, productLikeOrDislikeDTO.getReviewId(), -1);
                        redisUtil.incrCacheMapValue(dislikeKey, productLikeOrDislikeDTO.getReviewId(), 1);
                    } else {
                        throw new RuntimeException("actionType参数错误");
                    }
                }
            }
            // 场景2.2：历史记录状态为已取消（status=0）
            else {
                // 重新激活记录：更新操作类型并设置为有效状态
                likeRecord.setActionType(currentAction);
                likeRecord.setStatus(1); // 设置为有效
                likeRecord.setUpdatedAt(now);
                productReviewLikeDislikeMapper.updateById(likeRecord);

                // Redis缓存数更新：当前操作类型计数+1
                if (currentAction == 1) {
                    redisUtil.incrCacheMapValue(likeKey, productLikeOrDislikeDTO.getReviewId(), 1);
                } else if (currentAction == 0) {
                    redisUtil.incrCacheMapValue(dislikeKey, productLikeOrDislikeDTO.getReviewId(), 1);
                } else {
                    throw new RuntimeException("actionType参数错误");
                }
            }
        }
    }


    /**
     * 更新Redis统计的点赞或踩数到数据库
     * 逻辑：
     * 1. 获取前一天的日期由此拼接为Redis的key
     * 2. 根据key从Redis获取前一天的点赞和点踩数据
     * 3. 创建合并数据集（包含所有需要更新的评论ID）
     * 4. 准备批量更新数据列表
     * 5. 计算批处理次数（每批100条）
     * 6. 创建同步计数器countDownLatch
     * 7. 分批处理,使用线程池异步执行
     * 8. 等待所有批次完成
     * 9. 删除Redis中的数据
     */
    @Override
    public void updateCommentLikeOrDislikeTask() {
        // 1. 获取前一天的日期（格式：yyyyMMdd）
        //String yesterday = DateUtil.format(DateUtil.yesterday(), "yyyyMMdd");
        //测试用，实际运行时，取昨天的日期
        String yesterday = DateUtil.format(new Date(), "yyyyMMdd");

        // 2. 从Redis获取前一天的点赞和点踩数据
        String likeKey = String.format("comment_like:%s", yesterday);
        String dislikeKey = String.format("comment_dislike:%s", yesterday);
        Map<String, Object> likeCacheMap = redisUtil.getCacheMap(likeKey);
        Map<String, Object> dislikeCacheMap = redisUtil.getCacheMap(dislikeKey);

        // 3. 创建合并数据集（包含所有需要更新的评论ID）
        Set<String> allReviewIds = new HashSet<>();
        allReviewIds.addAll(likeCacheMap.keySet());
        allReviewIds.addAll(dislikeCacheMap.keySet());

        // 4. 准备批量更新数据列表
        List<ReviewLikeDislikeUpdateBO> updateList = new ArrayList<>();
        for (String reviewId : allReviewIds) {
            // 获取点赞增量（不存在则为0）
            int likeIncrement = Optional.ofNullable(likeCacheMap.get(reviewId))
                    .map(val -> Integer.parseInt(val.toString()))
                    .orElse(0);

            // 获取点踩增量（不存在则为0）
            int dislikeIncrement = Optional.ofNullable(dislikeCacheMap.get(reviewId))
                    .map(val -> Integer.parseInt(val.toString()))
                    .orElse(0);

            // 创建更新对象
            ReviewLikeDislikeUpdateBO update = new ReviewLikeDislikeUpdateBO();
            update.setReviewId(reviewId);
            update.setLikeIncrement(likeIncrement);
            update.setDislikeIncrement(dislikeIncrement);
            updateList.add(update);
        }

        // 5. 计算批处理次数（每批100条）
        int totalSize = updateList.size();
        int batchCount = (totalSize % 100 == 0) ? totalSize / 100 : totalSize / 100 + 1;

        // 6. 创建同步计数器
        CountDownLatch countDownLatch = new CountDownLatch(batchCount);

        // 7. 分批处理
        List<ReviewLikeDislikeUpdateBO> batchList = new ArrayList<>(100);
        for (int i = 0; i < totalSize; i++) {
            batchList.add(updateList.get(i));

            // 达到批次大小或最后一批时提交任务
            if (batchList.size() == 100 || i == totalSize - 1) {
                // 创建当前批次的副本（避免线程间干扰）
                final List<ReviewLikeDislikeUpdateBO> currentBatch = new ArrayList<>(batchList);


                // 提交到线程池执行,使用自定义的线程池
                ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                        10, 10, 0L, TimeUnit.MILLISECONDS,
                        new LinkedBlockingQueue<>(100),
                        new ThreadPoolExecutor.CallerRunsPolicy());

                threadPoolExecutor.execute(() -> {
                    try {
                        // 执行批量更新
                        productReviewMapper.batchUpdateLikeDislike(currentBatch);
                    } catch (Exception e) {
                        // 异常处理（可添加日志记录）
                        log.error("批量更新评论点赞点踩异常", e);
                        throw new RuntimeException(e);
                    } finally {
                        // 计数器减1
                        countDownLatch.countDown();
                    }
                });

                // 清空当前批次
                batchList.clear();
            }
        }

        // 8. 等待所有批次完成
        try {
            countDownLatch.await(30, TimeUnit.SECONDS); // 设置超时时间防止死锁
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("等待批处理任务完成时被中断", e);
        }

        // 9. 删除Redis中的数据
        redisUtil.deleteObject(likeKey);
        redisUtil.deleteObject(dislikeKey);
    }


}
